#pragma once

#include "../Core/Ray.h"
#include "../Core/Transform.h"
#include "../Core/BBox.h"

namespace krt {

	struct Intersection;

	enum class KRT_API SdfRecordType {
		_FLASE,
		_TRUE,
		_A,
		_B
	};

	class KRT_API ISdfBase {
	public:
		ISdfBase(Ref<Transform> o2w, Ref<Transform> w2o, bool ro)
			: objectToWorld(o2w), worldToObject(w2o), reverseOrientation(ro)
		{}

		virtual bool Intersect(const Ray& r, float& tHit, float& epsilon, Intersection& dg, SdfRecordType& sdfRec) const = 0;

		virtual bool Sdf(const glm::vec3& p, float& sdfResult) const = 0;

		virtual BBox GetBBox() const = 0;

		// geometry shading
		virtual void GetShadingGeometry(const Transform& o2w,
			const Intersection& dg,
			Intersection& dgShading)const {
		}

		// surface area
		virtual float Area() const { return .0f; }

	protected:
		Ref<Transform> objectToWorld, worldToObject;

		// reverse normal or not
		bool reverseOrientation;

	};

	class KRT_API SdfSphere : public ISdfBase {
	public:
		SdfSphere(Ref<Transform> o2w, Ref<Transform> w2o, bool ro,
			float radius = 1.0f)
			: ISdfBase(o2w, w2o, ro), radius(radius)
		{}

		virtual bool Intersect(const Ray& r, float& tHit, float& epsilon, Intersection& dg, SdfRecordType& sdfRec) const override;

		virtual bool Sdf(const glm::vec3& p, float& sdfResult) const override;

		virtual BBox GetBBox() const;

	public:
		virtual float Area() const override { return glm::pi<float>() * 4.0f * radius * radius; }

	private:
		static glm::vec2 GetSphereUV(const glm::vec3& normal);

		float radius;

		constexpr static float phiMax = glm::radians(360.0f);

	};

	class KRT_API SdfTriangle : public ISdfBase {
	public:
		SdfTriangle(Ref<Transform> o2w, Ref<Transform> w2o, bool ro)
			: ISdfBase(o2w, w2o, ro)
		{}

		virtual bool Intersect(const Ray& r, float& tHit, float& epsilon, Intersection& dg, SdfRecordType& sdfRec) const override;

		virtual bool Sdf(const glm::vec3& p, float& sdfResult) const override;

		virtual BBox GetBBox() const;

	};

	class KRT_API SdfUnion : public ISdfBase {
	public:
		SdfUnion(Ref<Transform> o2w, Ref<Transform> w2o, bool ro)
			: ISdfBase(o2w, w2o, ro)
		{}

		virtual bool Intersect(const Ray& r, float& tHit, float& epsilon, Intersection& dg, SdfRecordType& sdfRec) const override;

		virtual bool Sdf(const glm::vec3& p, float& sdfResult) const override;

		virtual BBox GetBBox() const;

	};
}